<?php
/**
 * PureMVC Port to PHP originally translated by Asbjørn Sloth Tønnesen
 *
 * @author Omar Gonzalez :: omar@almerblank.com
 * @author Hasan Otuome :: hasan@almerblank.com
 *
 * Created on Sep 24, 2008
 * PureMVC - Copyright(c) 2006-2008 Futurescale, Inc., Some rights reserved.
 * Your reuse is governed by the Creative Commons Attribution 3.0 Unported License
 */

/**
 * A Singleton <code>PureMVC_View_Interface</code> implementation.
 *
 * <P>
 * In PureMVC, the <code>View</code> class assumes these responsibilities:
 * <UL>
 * <LI>Maintain a cache of <code>IMediator</code> instances.</LI>
 * <LI>Provide methods for registering, retrieving, and removing <code>IMediators</code>.</LI>
 * <LI>Managing the observer lists for each <code>PureMVC_Observer_Notification_Interface</code> in the application.</LI>
 * <LI>Providing a method for attaching <code>PureMVC_Observer_Interface</code> to an <code>PureMVC_Observer_Notification_Interface</code>'s observer list.</LI>
 * <LI>Providing a method for broadcasting an <code>PureMVC_Observer_Notification_Interface</code>.</LI>
 * <LI>Notifying the <code>PureMVC_Observer_Interface</code> of a given <code>PureMVC_Observer_Notification_Interface</code> when it broadcast.</LI>
 * </UL>
 *
 * @package org.puremvc.php.core.View
 * @see org.puremvc.php.patterns.mediator.Mediator Mediator
 * @see org.puremvc.php.patterns.observer.Observer Observer
 * @see org.puremvc.php.patterns.observer.Notification Notification
 */
class PureMVC_View implements PureMVC_View_Interface
{

	// Mapping of Mediator names to Mediator instances
	protected $_mediatorMap;

	// Mapping of Notification names to Observer lists
	protected $_observerMap;

	// Singleton instance
	protected static $_instance;

	/**
	 * Constructor.
	 *
	 * <P>
	 * This <code>PureMVC_View_Interface</code> implementation is a Singleton,
	 * so you should not call the constructor
	 * directly, but instead call the static Singleton
	 * Factory method <code>View.getInstance()</code>
	 *
	 * @throws Error Error if Singleton instance has already been constructed
	 *
	 */
	private function __construct()
	{
		$this->_mediatorMap = array();
		$this->_observerMap = array();
		$this->_initializeView();
	}

	/**
	 * Initialize the Singleton View instance.
	 *
	 * <P>
	 * Called automatically by the constructor, this
	 * is your opportunity to initialize the Singleton
	 * instance in your subclass without overriding the
	 * constructor.</P>
	 *
	 * @return void
	 */
	protected function _initializeView()
	{
	}

	/**
	 * View Singleton Factory method.
	 *
	 * @return the Singleton instance of <code>View</code>
	 */
	public static function getInstance()
	{
		if ( self::$_instance == null )
			self::$_instance = new self( );
		return self::$_instance;
	}

	/**
	 * Register an <code>PureMVC_Observer_Interface</code> to be notified
	 * of <code>PureMVC_Observer_Notification_Interfaces</code> with a given name.
	 *
	 * @param notificationName the name of the <code>PureMVC_Observer_Notification_Interfaces</code> to notify this <code>IObserver</code> of
	 * @param observer the <code>PureMVC_Observer_Interface</code> to register
	 */
	public function registerObserver( $notificationName, PureMVC_Observer_Interface $observer )
	{
		if ( isset( $this->_observerMap[ $notificationName ] ) && $this->_observerMap[ $notificationName ] != null )
		{
			array_push( $this->_observerMap[ $notificationName ], $observer );
		}
		else
		{
			$this->_observerMap[ $notificationName ] = array( $observer );
		}
	}

	/**
	 * Notify the <code>PureMVC_Observer_Interface</code> for a particular <code>PureMVC_Observer_Notification_Interface</code>.
	 *
	 * <P>
	 * All previously attached <code>PureMVC_Observer_Interface</code> for this <code>PureMVC_Observer_Notification_Interface</code>'s
	 * list are notified and are passed a reference to the <code>PureMVC_Observer_Notification_Interface</code> in
	 * the order in which they were registered.</P>
	 *
	 * @param notification the <code>PureMVC_Observer_Notification_Interface</code> to notify <code>PureMVC_Observer_Interface</code> of.
	 */
	public function notifyObservers( PureMVC_Observer_Notification_Interface $notification )
	{
		if ( isset( $this->_observerMap[ $notification->getName() ] ) )
		{
			$observers = $this->_observerMap[ $notification->getName() ];
			foreach ( $observers as $observer )
			{
				$observer->notifyObserver( $notification );
			}
		}
	}

	/**
	 * Register an <code>IMediator</code> instance with the <code>View</code>.
	 *
	 * <P>
	 * Registers the <code>IMediator</code> so that it can be retrieved by name,
	 * and further interrogates the <code>IMediator</code> for its
	 * <code>PureMVC_Observer_Notification_Interface</code> interests.</P>
	 * <P>
	 * If the <code>IMediator</code> returns any <code>PureMVC_Observer_Notification_Interface</code>
	 * names to be notified about, an <code>Observer</code> is created encapsulating
	 * the <code>IMediator</code> instance's <code>handleNotification</code> method
	 * and registering it as an <code>Observer</code> for all <code>PureMVC_Observer_Notification_Interfaces</code> the
	 * <code>IMediator</code> is interested in.</p>
	 *
	 * @param mediatorName the name to associate with this <code>IMediator</code> instance
	 * @param mediator a reference to the <code>IMediator</code> instance
	 */
	public function registerMediator( PureMVC_Mediator_Interface $mediator )
	{
		// Register the Mediator for retrieval by name
		$this->_mediatorMap[ $mediator->getMediatorName() ] = $mediator;

		// Get Notification interests, if any.
		$interests = $mediator->listNotificationInterests();

		if ( count( $interests ) > 0 )
		{
			// Create Observer
			$observer = new PureMVC_Observer( "handleNotification", $mediator );

			// Register Mediator as Observer for its list of Notification interests
			foreach ( $interests as $interest )
			{
				$this->registerObserver( $interest, $observer );
			}
		}

		// Alert the Mediator that it has been registered
		$mediator->onRegister();
	}

	/**
	 * Retrieve an <code>IMediator</code> from the <code>View</code>.
	 *
	 * @param mediatorName the name of the <code>IMediator</code> instance to retrieve.
	 * @return the <code>IMediator</code> instance previously registered with the given <code>mediatorName</code>.
	 */
	public function retrieveMediator( $mediatorName )
	{
		$result = null;
		if ( $this->hasMediator( $mediatorName ) )
		{
			$result = $this->_mediatorMap[ $mediatorName ];
		}
		return $result;
	}

	/**
	 * Check to see if a Mediator is registered with the View.
	 *
	 * @param mediatorName name of the <code>IMediator</code> instance to check for.
	 */
	public function hasMediator( $mediatorName )
	{
		$result = false;
		if ( !array_key_exists( $mediatorName, $this->_mediatorMap ) )
		{
			$result = false;
		}
		else
		{
			$result = $this->_mediatorMap[ $mediatorName ] != null;
		}
		return $result;
	}

	/**
	 * Remove an <code>IMediator</code> from the <code>View</code>.
	 *
	 * @param mediatorName name of the <code>IMediator</code> instance to be removed.
	 */
	public function removeMediator( $mediatorName )
	{
		// Remove all Observers with a reference to this Mediator
		// also, when an notification's observer list length falls to
		// zero, remove it.
		foreach ( $this->_observerMap as &$observers )
		{
			foreach ( $observers as &$observer )
			{
				if ( $observer->compareNotifyContext( $this->retrieveMediator( $mediatorName ) ) == true )
				{
					unset( $observer );

					if ( count( $observers ) == 0 )
					{
						unset( $observers );
						break;
					}
				}
			}
		}

		$mediator = null;
		if ( $this->hasMediator( $mediatorName ) )
		{
			// get a reference to the mediator to be removed
			$mediator = $this->_mediatorMap[ $mediatorName ];

			// Remove the reference from the map
			unset( $this->_mediatorMap[ $mediatorName ] );

			// alert the mediator that it has been removed
			if ( $mediator != null )
			{
				$mediator->onRemove();
			}
		}
		return $mediator;
	}
}